﻿using System;
using System.Linq;
using UnityEngine;
using System.Collections.Generic;

namespace Miao
{
    /// <summary>
    /// 提供各类随机操作接口.
    /// 该类是对 <see cref="UnityEngine.Random"/> 的高层封装
    /// </summary>
    public static class RandomW
    {
        /// <summary>
        /// 设置随机种子
        /// </summary>
        /// <param name="randomSeed"></param>
        public static void SetSeed(int randomSeed)
        {
            UnityEngine.Random.InitState(randomSeed);
        }
        /// <summary>
        /// 设置状态(随机种子)
        /// </summary>
        /// <param name="randomState"></param>
        public static void SetState(UnityEngine.Random.State randomState)
        {
            UnityEngine.Random.state = randomState;
        }
        /// <summary>
        /// 获取状态(随机种子)
        /// </summary>
        /// <returns></returns>
        public static UnityEngine.Random.State GetState()
        {
            return UnityEngine.Random.state;
        }

        /// <summary>
        /// 随机一个 <see cref="int"/> 值.
        /// [0,最大值)
        /// </summary>
        /// <param name="maxValue"></param>
        /// <returns></returns>
        public static int RandomInt(int maxValue)
        {
            return UnityEngine.Random.Range(0, maxValue);
        }
        /// <summary>
        /// 随机一个 <see cref="int"/> 值.
        /// [最小值,最大值)
        /// </summary>
        /// <param name="minValue"></param>
        /// <param name="maxValue"></param>
        /// <returns></returns>
        public static int RandomInt(int minValue, int maxValue)
        {
            return UnityEngine.Random.Range(minValue, maxValue);
        }
        /// <summary>
        /// 从 -1 和 1 中随机挑选一个
        /// </summary>
        /// <returns></returns>
        public static int RandomIntNormalized()
        {
            return 2 * RandomInt(2) - 1;
        }
        /// <summary>
        /// 从区间内基于概率来随机获取一个值.
        /// 如果概率为1, 返回<paramref name="maxValue"/>; 
        /// 如果概率为0, 返回<paramref name="minValue"/>; 
        /// 如果概率为(0,1), 则按照轮盘赌策略来返回(<paramref name="minValue"/>, <paramref name="maxValue"/>)的值
        /// </summary>
        /// <param name="minValue"></param>
        /// <param name="maxValue"></param>
        /// <param name="probability"></param>
        /// <returns></returns>
        public static int GetValueBasedOnProbability(int minValue, int maxValue, float probability)
        {
            int result = minValue;
            int length = maxValue - minValue;
            for (int i = 0; i < length; i++) if (RandomFloat01() <= probability) result++;
            return result;
        }

        /// <summary>
        /// 随机选数.
        /// 从区间 [<paramref name="minValue"/>, <paramref name="maxValue"/>) 内挑选 <paramref name="count"/> 个数
        /// </summary>
        /// <param name="minValue"></param>
        /// <param name="maxValue"></param>
        /// <param name="count"></param>
        /// <returns></returns>
        public static int[] SelectNumber(int minValue, int maxValue, int count)
        {
            int length = maxValue - minValue;
            if (count > length) throw new Exception($"{nameof(RandomW)}.{nameof(SelectNumber)}: {nameof(count)} 不能超过区间范围");
            int[] interval = MathW.Interval(minValue, maxValue);
            int[] result = new int[count];
            for (int i = 0; i < count; i++)
            {
                int randomIndex = RandomInt(length);
                result[i] = interval[randomIndex];
                interval[randomIndex] = interval[--length];
            }
            return result;
        }
        /// <summary>
        /// 随机选取.
        /// 从指定的集合 <paramref name="allSelectionObjects"/> 中随机选取 <paramref name="selectionNumber"/> 个元素
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="allSelectionObjects">要选取的集合</param>
        /// <param name="selectionNumber">要从集合中获取的元素数量</param>
        /// <returns></returns>
        public static T[] RandomSelection<T>(IList<T> allSelectionObjects, int selectionNumber)
        {
            int length = allSelectionObjects.Count;
            if (length == 0) return null;
            if (selectionNumber > length) throw new Exception($"{nameof(RandomW)}.{nameof(RandomSelection)}: 随机选择的数量超过了可选择的最大数量!");
            int[] indexs = new int[length];
            for (int i = 0; i < length; i++) indexs[i] = i;
            T[] result = new T[selectionNumber];
            for (int i = 0; i < selectionNumber; i++)
            {
                int index = RandomInt(length);
                result[i] = allSelectionObjects[indexs[index]];
                indexs[index] = indexs[--length];
            }
            return result;
        }
        public static List<T> RandomSelection<T>(IList<T> selectionObjects, int selectionNumber, Func<T, List<T>, bool> selectFunc)
        {
            T[] selectionObjectsArray = selectionObjects.ToArray();
            int length = selectionObjectsArray.Length;
            if (length == 0) return null;
            if (selectionNumber > length) throw new Exception("[Random.RandomSelection] Error : selectionNumber > Number of allSelectionObject!");
            int[] objectIndexes = new int[length];
            for (int i = 0; i < length; i++) objectIndexes[i] = i;
            List<T> result = new List<T>(selectionNumber);
            int selectedNumber = 0;
            while (selectedNumber < selectionNumber)
            {
                if (length == 0) throw new Exception("[Random.RandomSelection] Number of available objects less selectionNumber,Please reduce selectionNumber or relax selectFunc!");
                int randomIndex = RandomInt(length);
                int objectIndex = objectIndexes[randomIndex];
                T selectionObject = selectionObjectsArray[objectIndex];
                if (selectFunc(selectionObject, result))
                {
                    selectedNumber++;
                    result.Add(selectionObject);
                }
                objectIndexes[randomIndex] = objectIndexes[--length];
            }
            return result;
        }
        /// <summary>
        /// 随机选择一个元素
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="selectionObjects"></param>
        /// <returns></returns>
        public static T RandomSelectOne<T>(IEnumerable<T> selectionObjects)
        {
            int count = selectionObjects.Count();
            if (count == 0) throw new Exception($"{nameof(RandomW)}.{nameof(RandomSelectOne)} 错误:传入的数据不能为 0 个!");
            int randomIndex = RandomInt(count);
            int currentIndex = 0;
            foreach (var selectionObject in selectionObjects) if (currentIndex++ == randomIndex) return selectionObject;
            return default;
        }
        /// <summary>
        /// 从 <paramref name="object1"/> 和 <paramref name="object2"/> 中随机挑选一个
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="object1"></param>
        /// <param name="object2"></param>
        /// <returns></returns>
        public static T RandomSelectOne<T>(T object1, T object2)
        {
            return RandomInt(2) == 0 ? object1 : object2;
        }
        /// <summary>
        /// 洗牌.
        /// 保证每个元素都不在原来的位置上
        /// </summary>
        /// <param name="set">要进行洗牌操作的集合</param>
        public static void Shuffle<T>(IList<T> set)
        {
            for (int i = set.Count - 1; i > 0; i--)
            {
                int swapIndex = RandomInt(i);
                T element = set[swapIndex];
                set[swapIndex] = set[i];
                set[i] = element;
            }
        }
        /// <summary>
        /// 用指定的长度和总和值随机一个 <see cref="float"/>数组，效果不好，待优化
        /// </summary>
        /// <param name="arrayLength"></param>
        /// <param name="elementSumValue"></param>
        /// <returns></returns>
        public static float[] RandomArray(int arrayLength, float elementSumValue)
        {
            float[] result = new float[arrayLength];
            for (int i = 0; i < arrayLength; i++)
            {
                float currentValue;
                if (i == arrayLength - 1) currentValue = elementSumValue;
                else currentValue = RandomFloat(0, elementSumValue / 2);//除以2防止后面的数太小
                elementSumValue -= currentValue;
                result[i] = currentValue;
            }
            return result;
        }

        /// <summary>
        /// 随机一个 bool 值
        /// </summary>
        /// <returns></returns>
        public static bool RandomBool()
        {
            return RandomInt(2) == 0;
        }
        /// <summary>
        /// 随机一个 <see cref="Vector3"/> 向量
        /// </summary>
        /// <returns></returns>
        public static Vector3 RandomVector3(float minValue, float maxValue)
        {
            return new Vector3(RandomFloat(minValue, maxValue), RandomFloat(minValue, maxValue), RandomFloat(minValue, maxValue));
        }
        /// <summary>
        /// 随机一个 <see cref="Vector2"/> 向量
        /// </summary>
        /// <returns></returns>
        public static Vector2 RandomVector2(float minValue, float maxValue)
        {
            return new Vector2(RandomFloat(minValue, maxValue), RandomFloat(minValue, maxValue));
        }
        /// <summary>
        /// 随机一个 <see cref="Vector3"/> 方向
        /// </summary>
        /// <returns></returns>
        public static Vector3 RandomDirection()
        {
            return RandomVector3(-1f, 1f).normalized;
        }
        /// <summary>
        /// 随机一个 <see cref="Vector2"/> 方向
        /// </summary>
        /// <returns></returns>
        public static Vector2 RandomDirection2D()
        {
            return RandomVector2(-1f, 1f).normalized;
        }
        public static Vector3 RandomDirection(Vector3 startDirection, float maxAngle, Vector3 axis)
        {
            float randomAngle = RandomFloat(-maxAngle, maxAngle);
            return Quaternion.AngleAxis(randomAngle, axis) * startDirection;
        }
        /// <summary>
        /// 从 <paramref name="startDirection"/> 和 <paramref name="endDirection"/> 组成的插值区间中随机选取一个 <see cref="Vector3"/> 方向
        /// </summary>
        /// <param name="startDirection"></param>
        /// <param name="endDirection"></param>
        /// <returns></returns>
        public static Vector3 RandomDirection(Vector3 startDirection, Vector3 endDirection)
        {
            return Vector3.Lerp(startDirection, endDirection, UnityEngine.Random.value).normalized;
        }
        /// <summary>
        /// 从 <paramref name="startDirection"/> 和 <paramref name="endDirection"/> 组成的插值区间中随机选取一个 <see cref="Vector2"/> 方向
        /// </summary>
        /// <param name="startDirection"></param>
        /// <param name="endDirection"></param>
        /// <returns></returns>
        public static Vector2 RandomVector2Direction(Vector2 startDirection, Vector2 endDirection)
        {
            return Vector2.Lerp(startDirection, endDirection, UnityEngine.Random.value).normalized;
        }
        public static Vector2 RandomVector2Direction(Vector2 startDirection, float maxAngle)
        {
            float randomAngle = RandomFloat(-maxAngle, maxAngle);
            return Quaternion.AngleAxis(randomAngle, Vector3.forward) * startDirection;
        }

        /// <summary>
        /// 随机一个 [0,1] 的<see cref="float"/> 值.
        /// </summary>
        /// <returns></returns>
        public static float RandomFloat01()
        {
            return UnityEngine.Random.value;
        }
        /// <summary>
        /// 随机一个 <see cref="float"/> 值.
        /// [0, <paramref name="maxValue"/>]
        /// </summary>
        /// <param name="maxValue"></param>
        /// <returns></returns>
        public static float RandomFloat(float maxValue)
        {
            return UnityEngine.Random.Range(0, maxValue);
        }
        /// <summary>
        /// 随机一个 <see cref="float"/> 值.
        /// [<paramref name="minValue"/>, <paramref name="maxValue"/>]
        /// </summary>
        /// <param name="minValue"></param>
        /// <param name="maxValue"></param>
        /// <returns></returns>
        public static float RandomFloat(float minValue, float maxValue)
        {
            return UnityEngine.Random.Range(minValue, maxValue);
        }
        /// <summary>
        /// 随机一个 [-1,1] 的<see cref="float"/> 值.
        /// </summary>
        /// <returns></returns>
        public static float RandomFloatNormalized()
        {
            return UnityEngine.Random.Range(-1f, 1f);
        }

        /// <summary>
        /// 随机一个在 <paramref name="camera"/> 视锥体中的点
        /// </summary>
        /// <param name="camera"></param>
        /// <returns></returns>
        public static Vector3 RandomPointInViewFrustum(Camera camera)
        {
            float nearPlaneDistance = camera.nearClipPlane;
            float farPlaneDistance = camera.farClipPlane;
            float zDistance = RandomFloat(nearPlaneDistance, farPlaneDistance);
            float scale = MathW.Mapping(zDistance, nearPlaneDistance, farPlaneDistance, 0f, 1f);
            float tanValue = MathW.TanDeg(camera.fieldOfView * 0.5f);
            float halfHeightNear = tanValue * nearPlaneDistance;//半近平面高度
            float halfHeightFar = tanValue * farPlaneDistance;//半远平面高度
            float halfY = MathW.Lerp(halfHeightNear, halfHeightFar, scale);//Y轴取值范围
            float yDistance = RandomFloat(-halfY, halfY);
            float halfWidthNear = halfHeightNear * camera.aspect;//半近平面宽度
            float halfWidthFar = halfHeightFar * camera.aspect;//半远平面宽度
            float halfX = MathW.Lerp(halfWidthNear, halfWidthFar, scale);
            float xDistance = RandomFloat(-halfX, halfX);
            Transform cameraTransform = camera.transform;
            return cameraTransform.position + cameraTransform.right * xDistance + cameraTransform.up * yDistance + cameraTransform.forward * zDistance;
        }
    }
}